/*
* Copyright (C) Microsoft. All rights reserved.  
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not 
* use this file except in compliance with the License.  You may obtain a copy 
* of the License at http://www.apache.org/licenses/LICENSE-2.0  
* 
* THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED 
* WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 
* MERCHANTABLITY OR NON-INFRINGEMENT. 
*
* See the Apache License, Version 2.0 for specific language governing 
* permissions and limitations under the License.
*
*/
#pragma once

#include <baja/common.hpp>
#include <baja/math.hpp>

namespace baja { namespace graphics {

namespace m = baja::math;

enum class shell_type : uint32
{
    directUI = 0x1,
    basic = 0x2,
};

// event handlers arguments
struct event_args
{
    event_args(void* sender_) : sender(sender_) {}
    void* sender;
};

struct resize_event_args : public event_args
{
    resize_event_args(void* sender_) : event_args(sender_) {}
    int32 width;
    int32 height;
};

// other types
typedef memory* handle;
typedef uint32 packed_color;

BAJA_BEGIN_CONSTANTS(constants)
    BAJA_DECLARE_CONST(uint32, maxTextures, 8)
    BAJA_DECLARE_CONST(uint32, maxLights, 4)
    BAJA_DECLARE_CONST(handle, handleInvalid, (handle)-1)
BAJA_END_CONSTANTS

// globals
BAJA_BEGIN_GLOBALS(globals)
    BAJA_DECLARE_CONST_GLOBAL(wchar*, standardVertexDeclName_LitColoredTextured)
    BAJA_DECLARE_CONST_GLOBAL(wchar*, standardVertexDeclName_ScreenColoredTextured)
    BAJA_DECLARE_CONST_GLOBAL(wchar*, standardVertexDeclName_Mesh)
    BAJA_DECLARE_CONST_GLOBAL(wchar*, standardEffectName_Mesh)
BAJA_END_GLOBALS(globals)

BAJA_DEFINE_CONST_GLOBAL(globals, wchar*, standardVertexDeclName_LitColoredTextured, L"VertexDeclaration.LitColoredTextured")
BAJA_DEFINE_CONST_GLOBAL(globals, wchar*, standardVertexDeclName_ScreenColoredTextured, L"VertexDeclaration.ScreenColoredTextured")
BAJA_DEFINE_CONST_GLOBAL(globals, wchar*, standardVertexDeclName_Mesh, L"VertexDeclaration.Mesh")
BAJA_DEFINE_CONST_GLOBAL(globals, wchar*, standardEffectName_Mesh, L"Effect.Mesh")

// enumerations

// blend state 
enum class blend_state : int32
{
    _min = 0,
    zero = 0,
    one = 1,
    srcColor = 2,
    invSrcColor = 3,
    srcAlpha = 4,
    invSrcAlpha = 5,
    dstAlpha = 6,
    invDstAlpha = 7,
    dstColor = 8,
    invDstColor = 9,
    srcAlphaSaturate = 10,
    _max = 10,
    force32Bit = 0x7fffffff, 
};

// blend op state 
enum class blend_op_state : int32
{
    _min = 0,
    add = 0,
    subtract = 1,
    reverseSubtract = 2,
    min = 3,
    max = 4,
    _max = 4,
    force32Bit = 0x7fffffff, 
};

// clear targets
enum class clear_targets : int32
{
    _min = 0,
    color = 0,
    colorAndDepth = 1,
    colorDepthAndStencil = 2,
    _max = 2,
    force32Bit = 0x7fffffff, 
};

// compare state 
enum class compare_state : int32
{
    _min = 0,
    never = 0,
    less = 1,
    equal = 2,
    lessEqual = 3,
    greater = 4,
    notEqual = 5,
    greaterEqual = 6,
    always = 7,
    _max = 7,
    force32Bit = 0x7fffffff, 
};

// cull state
enum class cull_state: int32
{
    _min = 0,
    none = 0,
    clockwise = 1,
    counterClockwise = 2,
    _max = 2,
    force32Bit = 0x7fffffff, 
};

// enabled state 
enum class enabled_state: int32
{
    _min = 0,
    disabled = 0,
    enabled = 1,
    _max = 1,
    force32Bit = 0x7fffffff, 
};

// file formats
enum class file_format : int32
{
    _min = 0,
    unknown = 0,
    BMP = 1,
    JPG = 2,
    PNG = 3,
    GIF = 4,
    TGA = 5,
    TIFF = 6,
    DDS = 7,
    _max = 7,
    force32Bit = 0x7fffffff, 
};

// fill mode
enum class fill_mode : int32
{
    _min = 0,
    solid = 0,
    wireframe = 1,
    _max = 1,
};

// horizontal alignment
enum class horizontal_alignment : int32
{
    _min = 0,
    left = 0,
    center = 1,
    right = 2,
    _max = 2,
    force32Bit = 0x7fffffff, 
};

// vertical alignment
enum class vertical_alignment : int32
{
    _min = 0,
    top = 0,
    center = 1,
    bottom = 2,
    _max = 2,
    force32Bit = 0x7fffffff, 
};

// light types
enum class light_type : int32
{
    _min = 0,
    directional = 0,
    point = 1,
    _max = 1,
    force32Bit = 0x7fffffff, 
};

// locking behavior types
enum class lock_type : int32
{
    _min = 0,
    read = 0,
    write = 1,
    _max = 1,
    force32Bit = 0x7fffffff, 
};

// memory pool 
enum class memory_pool : int32
{
    _min = 0,
    default = 0,
    system = 1,
    _max = 1,
    force32Bit = 0x7fffffff, 
};

// mipmap load specification enum
enum class mipmap_load : int32
{
    _min = 0,
    noMipmaps = 0,
    forceMipmaps = 1,
    fromSource = 2,
    _max = 2,
    force32Bit = 0x7fffffff,
};

// pixel formats
enum class pixel_format : int32
{
    _min = 0,
    unknown = 0,
    d32BitColorWithAlpha = 1,
    d32BitColorNoAlpha = 2,
    d32BitTypeless = 3,
    f32BitColorWithAlpha = 4,
    f32BitTypeless = 5,
    placeholder4 = 6,
    placeholder5 = 7,
    d32 = 8,
    placeholder6 = 9,
    d24S8 = 10,
    dXT1 = 11,
    dXT2 = 12,
    dXT3 = 13,
    dXT4 = 14,
    dXT5 = 15,
    fromSource = 16,

    // The following 4 formats specify the byte order, which is needed when working 
    // directly with the data in memory. If you create a texture using the generic 
    // pixelFormat32BitColorWithAlpha or pixelFormat32BitColorNoAlpha formats, 
    // we use one that works for your graphics card. You can later query to get the 
    // format we picked.
    d32BitRGBA = 17,
    d32BitBGRA = 18,
    d32BitRGB  = 19,
    d32BitBGR  = 20,
    _max = 21,

    force32Bit = 0x7fffffff, 
};

// primitive types
enum class primitive_type : int32
{
    _min = 0,
    pointList = 0,
    lineList = 1,
    lineStrip = 2,
    triangleList = 3,
    triangleStrip = 4,
    _max = 4,
    force32Bit = 0x7fffffff, 
};

// rendering mode
enum class render_mode : int32
{
    _min = 0,
    normal = 0,
    wireframe = 1,
    overdraw = 2,
    _max = 2,
    force32Bit = 0x7fffffff,
};

// standard effects
enum class standard_effect : int32
{
    _min = 0,
    colored = 0,
    textured = 1,
    coloredTextured = 2,
    litColored = 3,
    litTextured = 4,
    litColoredTextured = 5,
    screenColored = 6,
    screenTextured = 7,
    screenColoredTextured = 8,
    meshUnlit = 9,
    meshLambert = 10,
    meshPhong = 11,
    total = 12,
    _max = 12,
    force32Bit = 0x7fffffff, 
};

// stencil op
enum class stencil_op : int32
{
    _min = 0,
    keep = 0,
    zero = 1,
    replace = 2,
    incrementClamp = 3,
    decrementClamp = 4,
    invert = 5,
    increment = 6,
    decrement = 7,
    _max = 7,
    force32Bit = 0x7fffffff, 
};

// texture filter types
enum class texture_filter : int32
{
    _min = 0,
    none = 0,
    point = 1,
    linear = 2,
    anisotropic = 3,
    _max = 3,
    force32Bit = 0x7fffffff,
};

// texture usage
enum class texture_usage : int32
{
    depthStencil = 0x0001,
    renderTarget = 0x0002,
    genMipMaps = 0x0004,
    dynamic = 0x0008,
    force32Bit = 0x7fffffff, 
};

enum class texture_type : int32
{
    undorderedAccess = 0,
    shaderResource = 1,
    renderTarget = 2,
    depthStencil = 3
};

// vertex buffer usage
enum class vertex_buffer_usage : int32
{
    _min = 0,
    default = 0,
    dynamic = 1,
    _max = 1,
    force32Bit = 0x7fffffff, 
};

// index buffer usage
enum class index_buffer_usage : int32
{
    _min = 0,
    default = 0,
    dynamic = 1,
    _max = 1,
    force32Bit = 0x7fffffff, 
};

// vertex declaration type
enum class vertex_decl_type : int32
{
    _min = 0,
    float1 = 0,
    float2 = 1,
    float3 = 2,
    float4 = 3,
    packedColor = 4,
    uint = 5,
    _max = 5,
    force32Bit = 0x7fffffff, 
};

// vertex declaration usage
enum class vertex_decl_usage : int32
{
    _min = 0,
    position = 0,
    blendWeight = 1,
    blendIndices = 2,
    normal = 3,
    pointSize = 4,
    texCoord = 5,
    tangent = 6,
    binormal = 7,
    tessellationFactor = 8,
    transformedPosition = 9,
    color = 10,
    fog = 11,
    depth = 12,
    _max = 12,
    force32Bit = 0x7fffffff, 
};

// color functions
inline packed_color packColor(const uint32 r, const uint32 g, const uint32 b) 
{ 
    return (0xff << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff); 
}

inline packed_color packColor(const uint32 a, const uint32 r, const uint32 g, const uint32 b) 
{ 
    return ((a & 0xff) << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff); 
}

inline packed_color packNormalizedColor(const float32 r, const float32 g, const float32 b) 
{ 
    return packColor((uint32)(r*255.0f), (uint32)(g*255.0f), (uint32)(b*255.0f)); 
}

inline packed_color packNormalizedColor(const float32 a, const float32 r, const float32 g, const float32 b) 
{ 
    return packColor((uint32)(a*255.0f), (uint32)(r*255.0f), (uint32)(g*255.0f), (uint32)(b*255.0f)); 
}

inline uint8 unpackColor_A(const packed_color c) 
{  
    return (c >> 24) & 0xff;
}

inline uint8 unpackColor_R(const packed_color c) 
{ 
    return (c >> 16) & 0xff;
}

inline uint8 unpackColor_G(const packed_color c) 
{ 
    return (c >> 8) & 0xff;
}

inline uint8 unpackColor_B(const packed_color c) 
{ 
    return (c >> 0) & 0xff;
}

// structures
struct display_params
{
    pixel_format backBufferFormat;
    pixel_format depthStencilFormat;
    uint32 backBufferWidth;
    uint32 backBufferHeight;
    uint32 backBufferCount;
    bool useFSAA;
};

struct color 
{
    float32 r;
    float32 g;
    float32 b;
    float32 a;

    inline color() : r(0.f), g(0.f), b(0.f), a(0.f) { }
    inline color(const color& c) : r(c.r), g(c.g), b(c.b), a(c.a) {}
    inline color(const float32 r_, const float32 g_, const float32 b_) : r(r_), g(g_), b(b_), a(1.f) {}
    inline color(const float32 a_, const float32 r_, const float32 g_, const float32 b_) : r(r_), g(g_), b(b_), a(a_) {}
    inline color(uint8 r_, uint8 g_, uint8 b_) : r(r_/255.f), g(g_/255.f), b(b_/255.f), a(1.f) {}
    inline color(uint8 a_, uint8 r_, uint8 g_, uint8 b_) : r(r_/255.f), g(g_/255.f), b(b_/255.f), a(a_/255.f) {}
    inline color(const packed_color pc) { this->setFromPackedColor(pc); }

    inline bool operator==(const color& that) const { return (this->a == that.a && this->r == that.r && this->g == that.g && this->b == that.b); }
    inline bool operator!=(const color& that) const { return !(*this == that); }

    inline packed_color packColorARGB() const { return packNormalizedColor(this->a, this->r, this->g, this->b); }
    inline packed_color packColorXRGB() const { return packNormalizedColor(this->r, this->g, this->b); }

    inline void set(const float32 a, const float32 r, const float32 g, const float32 b)
    {
        this->a = a;
        this->r = r; 
        this->g = g;
        this->b = b; 
    }

    void setFromPackedColor(const packed_color pc)
    {
        static const float32 colorRange = 255.f;
        static const float32 invColorRange = 1.f / colorRange;

        this->a = (float32)((pc & 0xff000000) >> 24) * invColorRange;
        this->r = (float32)((pc & 0x00ff0000) >> 16) * invColorRange;
        this->g = (float32)((pc & 0x0000ff00) >> 8)  * invColorRange;
        this->b = (float32) (pc & 0x000000ff)        * invColorRange;
    }

    void lerpTo(const color& c, const float32 t, color* result) const
    {
        result->a = this->a + t * (c.a - this->a);
        result->r = this->r + t * (c.r - this->r);
        result->g = this->g + t * (c.g - this->g);
        result->b = this->b + t * (c.b - this->b);
    }

    void normalize()
    {
        this->a = max(0.0f, min(1.0f, this->a));
        this->r = max(0.0f, min(1.0f, this->r));
        this->g = max(0.0f, min(1.0f, this->g));
        this->b = max(0.0f, min(1.0f, this->b));
    }
};

struct light
{
    light_type type;
    color color;
    m::point3d position;
    m::vector3d direction;
    float32 range;
    float32 specularIntensity;
    float32 attenuation0;
    float32 attenuation1;
    float32 attenuation2;
};

// texture_coordinate class. U is the horizontal axis in texture space (like X) and V is vertical axis in texture space (like Y). 
struct texture_coordinate 
{
    float32 u;
    float32 v;

    inline texture_coordinate() : u(0.f), v(0.f) {}
    inline texture_coordinate(const texture_coordinate& uv) : u(uv.u), v(uv.v) {}
    inline texture_coordinate(const float32 u_, const float32 v_) : u(u_), v(v_) {}

    inline
    texture_coordinate& operator=(const texture_coordinate& that)
    { 
        this->u = that.u;
        this->v = that.v;
        return *this; 
    }

    inline void set(const float32 u, const float32 v)
    {
        this->u = u;
        this->v = v;
    }

    void lerpTo(const texture_coordinate& uv, const float32 t, __out texture_coordinate* result) const
    {
        result->u = this->u + (t * (uv.u - this->u));
        result->v = this->v + (t * (uv.v - this->v));
    }
};

struct vertex_lit_colored_textured
{
    m::point3d position;
    m::vector3d normal;
    m::vector4d tangent;
    packed_color color;
    texture_coordinate uv[1];
};

typedef vertex_lit_colored_textured vertex_mesh;

struct vertex_screen_colored_textured
{
    m::point4d position;
    packed_color color;
    texture_coordinate uv[1];
};

struct viewport
{
    viewport() : position(0,0), width(0), height(0), minZ(0), maxZ(0) {}

    m::point2d position;
    float32 width;
    float32 height;
    float32 minZ;
    float32 maxZ;
};

struct render_transforms
{
    // camera
    m::matrix44 localToWorldSpace;
    m::matrix44 worldToLocalSpace;

    m::matrix44 worldToCameraSpace;
    m::matrix44 cameraToWorldSpace;

    m::matrix44 cameraToProjectedSpace;
    m::matrix44 projectedToCameraSpace;

    // final
    m::matrix44 worldToProjectedSpace; // camera
};

}} // namespace baja.graphics
